package day28.ThreadTest;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 生产者-消费者模型演示
 * 展示使用wait/notify实现线程间通信的经典示例
 * 使用队列作为缓冲区管理数据
 * 展示了多生产者多消费者场景下的线程协调
 * 
 * 知识点：
 * 1. 多线程编程 - Java中实现并发程序的基本方式
 * 2. 生产者-消费者模式 - 经典的并发设计模式
 * 3. 线程间通信 - 通过wait/notify机制实现线程协作
 * 4. synchronized关键字 - 实现方法级别的同步控制
 * 5. 线程安全 - 通过同步机制保证共享资源的安全访问
 */
public class ProducerConsumerDemo {
    public static void main(String[] args) {
        // 创建共享缓冲区
        Buffer buffer = new Buffer(5); // 缓冲区大小为5
        
        // 创建生产者和消费者线程
        // 知识点：Thread构造函数可以接受Runnable接口实现和线程名称
        Thread producer1 = new Thread(new Producer(buffer, "Producer-1"), "Producer-1");
        Thread producer2 = new Thread(new Producer(buffer, "Producer-2"), "Producer-2");
        Thread consumer1 = new Thread(new Consumer(buffer, "Consumer-1"), "Consumer-1");
        Thread consumer2 = new Thread(new Consumer(buffer, "Consumer-2"), "Consumer-2");
        
        // 启动所有线程
        // 知识点：start()方法启动线程，使其进入就绪状态等待CPU调度
        producer1.start();
        producer2.start();
        consumer1.start();
        consumer2.start();
    }
}

/**
 * 共享缓冲区类
 * 
 * 知识点：
 * 1. 线程同步 - 使用synchronized关键字保证同一时间只有一个线程访问临界区
 * 2. 等待/通知机制 - 使用Object类的wait()/notifyAll()方法实现线程间协调
 * 3. 临界资源 - 共享缓冲区是多个线程共同访问的资源，需要同步保护
 */
class Buffer {
    private Queue<Integer> queue;
    private int capacity;
    
    public Buffer(int capacity) {
        this.queue = new LinkedList<>();
        this.capacity = capacity;
    }
    
    /**
     * 生产方法 - 向缓冲区添加数据
     * @param item 要添加的数据
     * 
     * 知识点：
     * 1. synchronized方法 - 保证同一时间只有一个线程可以执行该方法
     * 2. wait()方法 - 使当前线程进入等待状态并释放对象锁
     * 3. notifyAll()方法 - 唤醒所有在该对象上等待的线程
     * 4. 线程状态转换 - 运行状态 <-> 等待状态 <-> 就绪状态
     */
    public synchronized void produce(int item) {
        // 如果缓冲区已满，则等待
        // 知识点：使用while循环而不是if判断是为了防止虚假唤醒
        while (queue.size() == capacity) {
            try {
                System.out.println(Thread.currentThread().getName() + " is waiting - buffer is full");
                // 知识点：wait()方法会释放当前对象锁，使线程进入等待状态
                wait(); // 释放锁并等待
            } catch (InterruptedException e) {
                // 知识点：处理中断异常，恢复中断状态
                Thread.currentThread().interrupt();
                return;
            }
        }
        
        // 添加数据到缓冲区
        queue.add(item);
        System.out.println(Thread.currentThread().getName() + " produced item: " + item + 
                          " (Buffer size: " + queue.size() + ")");
        
        // 通知等待的消费者线程
        // 知识点：notifyAll()唤醒所有等待线程，避免死锁问题
        notifyAll();
    }
    
    /**
     * 消费方法 - 从缓冲区取出数据
     * @return 从缓冲区取出的数据
     * 
     * 知识点：
     * 1. 同步方法 - 保证生产者和消费者不会同时访问缓冲区
     * 2. 等待/通知机制 - 实现线程间的协调配合
     * 3. 线程调度 - JVM根据线程优先级和调度算法决定线程执行顺序
     */
    public synchronized int consume() {
        // 如果缓冲区为空，则等待
        // 知识点：使用while循环检查条件，防止虚假唤醒导致的逻辑错误
        while (queue.isEmpty()) {
            try {
                System.out.println(Thread.currentThread().getName() + " is waiting - buffer is empty");
                // 知识点：wait()方法必须在同步代码块中调用
                wait(); // 释放锁并等待
            } catch (InterruptedException e) {
                // 知识点：处理中断异常的标准做法
                Thread.currentThread().interrupt();
                return -1;
            }
        }
        
        // 从缓冲区取出数据
        int item = queue.poll();
        System.out.println(Thread.currentThread().getName() + " consumed item: " + item + 
                          " (Buffer size: " + queue.size() + ")");
        
        // 通知等待的生产者线程
        // 知识点：使用notifyAll()而非notify()避免死锁和饥饿问题
        notifyAll();
        return item;
    }
}

/**
 * 生产者类
 * 
 * 知识点：
 * 1. Runnable接口 - 实现多线程的另一种方式
 * 2. 线程执行体 - run()方法中定义线程的具体执行逻辑
 * 3. 静态变量 - 在多个线程间共享，需要注意线程安全问题
 * 4. 线程休眠 - Thread.sleep()方法使线程暂停执行指定时间
 */
class Producer implements Runnable {
    private Buffer buffer;
    private String name;
    // 知识点：静态变量属于类，在所有实例间共享
    private static int itemCount = 1;
    
    public Producer(Buffer buffer, String name) {
        this.buffer = buffer;
        this.name = name;
    }
    
    @Override
    public void run() {
        try {
            // 每个生产者生产10个物品
            // 知识点：循环生产数据并交给缓冲区处理
            for (int i = 0; i < 10; i++) {
                // 知识点：++操作符在多线程环境下不是原子操作，此处存在线程安全问题
                buffer.produce(itemCount++);
                // 知识点：Thread.sleep()使当前线程休眠指定毫秒数，不释放锁
                Thread.sleep((int) (Math.random() * 100)); // 随机延迟
            }
        } catch (InterruptedException e) {
            // 知识点：处理线程中断异常的标准做法
            Thread.currentThread().interrupt();
        }
    }
}

/**
 * 消费者类
 * 
 * 知识点：
 * 1. Runnable接口实现 - 创建线程的常用方式
 * 2. 线程执行逻辑 - 在run()方法中定义具体的执行过程
 * 3. 异常处理 - 正确处理线程执行过程中可能出现的异常
 */
class Consumer implements Runnable {
    private Buffer buffer;
    private String name;
    
    public Consumer(Buffer buffer, String name) {
        this.buffer = buffer;
        this.name = name;
    }
    
    @Override
    public void run() {
        try {
            // 每个消费者消费10个物品
            // 知识点：循环从缓冲区获取数据进行消费
            for (int i = 0; i < 10; i++) {
                buffer.consume();
                // 知识点：随机休眠模拟不同的处理时间
                Thread.sleep((int) (Math.random() * 150)); // 随机延迟
            }
        } catch (InterruptedException e) {
            // 知识点：恢复线程的中断状态
            Thread.currentThread().interrupt();
        }
    }
}